package test;

import static bittwiddled.Dynamic.bitmask;
import static bittwiddled.InversionTools.calcActivation;
import static bittwiddled.InversionTools.calcNonActivation;
import static bittwiddled.InversionTools.calcNonInhibition;
import static bittwiddled.InversionTools.calcInhibition;
import static bittwiddled.BankerOrder.INTEGER.lessThan;
import static bittwiddled.BankerOrder.INTEGER.moreThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.junit.Before;
import org.junit.Test;

import bittwiddled.BankerOrder;
import bittwiddled.BeforeStates;
import bittwiddled.Dynamic;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;


public class BitTwiddled {

	private static final String[] input = {
		"10001000100",
		"01101000100",
		"01111000100",
		"01110000000",
		"01110001000",
		"01110001011",
		"00010011011",
		"00000010001",
		"00001110100",
		"00001100100",
		"00001000100",
		"00001000100"
	};

	private static final int[] rbars = {
		Integer.valueOf("01110011011",2).intValue(),
		Integer.valueOf("01110001011",2).intValue(),
		Integer.valueOf("11101110101",2).intValue(),
		Integer.valueOf("01110001011",2).intValue(),
		Integer.valueOf("01110011011",2).intValue(),
		Integer.valueOf("00001110101",2).intValue(),
		Integer.valueOf("11101110101",2).intValue(),
		Integer.valueOf("01111001111",2).intValue(),
		Integer.valueOf("11111001100",2).intValue(),
		Integer.valueOf("11111001100",2).intValue(),
		Integer.valueOf("10000000000",2).intValue()
	};
	
	private static final int[] gbars = {
		Integer.valueOf("01110010001",2).intValue(),
		Integer.valueOf("01110010000",2).intValue(),
		Integer.valueOf("00010000000",2).intValue(),
		Integer.valueOf("00000010000",2).intValue(),
		Integer.valueOf("01110001000",2).intValue(),
		Integer.valueOf("10001100100",2).intValue(),
		Integer.valueOf("00010000000",2).intValue(),
		Integer.valueOf("10001110100",2).intValue(),
		Integer.valueOf("00001100100",2).intValue(),
		Integer.valueOf("00001100100",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
	};
	
	private static final int[] gs = {
		Integer.valueOf("00000001000",2).intValue(),
		Integer.valueOf("00000001000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("00000010000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("10000000000",2).intValue(),
		Integer.valueOf("10000000000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue()
	};
	
	private static final int[] reducedGSize = {
		0, 0, 2, 1, 2, 0, 2, 1, 1, 1, 0
	};
	
	private static final List<Set<Integer>> reducedGs; static {
		reducedGs = ImmutableList.<Set<Integer>>builder()
			.add( ImmutableSet.<Integer>of() )
			.add( ImmutableSet.<Integer>of() )
			.add( ImmutableSet.<Integer>of(Integer.valueOf("00001000100",2), Integer.valueOf("00000010001",2)) )
			.add( ImmutableSet.<Integer>of(Integer.valueOf("01110000000",2) ) )
			.add( ImmutableSet.<Integer>of(Integer.valueOf("00000000011",2), Integer.valueOf("00000010001",2)) )
			.add( ImmutableSet.<Integer>of() )
			.add( ImmutableSet.<Integer>of(Integer.valueOf("00001000100",2), Integer.valueOf("00000010001",2)) )
			.add( ImmutableSet.<Integer>of(Integer.valueOf("01100000000",2) ) )
			.add( ImmutableSet.<Integer>of(Integer.valueOf("01100000000",2) ) )
			.add( ImmutableSet.<Integer>of(Integer.valueOf("01100000000",2) ) )
			.add( ImmutableSet.<Integer>of() )			
			.build();
	}
	
	private static final int[] rs = {
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("00000010000",2).intValue(),
		Integer.valueOf("00010000000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("00010000000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue(),
		Integer.valueOf("00000000000",2).intValue()
	};
	
	private static final List<Set<Integer>> reducedRs; static {
		reducedRs = ImmutableList.<Set<Integer>>builder()
			.add( ImmutableSet.<Integer>of() )
			.add( ImmutableSet.<Integer>of() )
			.add( ImmutableSet.<Integer>of() )
			.add( ImmutableSet.<Integer>of(Integer.valueOf("00001000100",2) ) )
			.add( ImmutableSet.<Integer>of() )
			.add( ImmutableSet.<Integer>of(Integer.valueOf("00010001010",2)) )
			.add( ImmutableSet.<Integer>of() )
			.add( ImmutableSet.<Integer>of() )
			.add( ImmutableSet.<Integer>of(Integer.valueOf("00000000011",2) ) )
			.add( ImmutableSet.<Integer>of(Integer.valueOf("00000000011",2) ) )
			.add( ImmutableSet.<Integer>of() )			
			.build();
	}
	
	private static final List<Map<Integer,Integer>> reducedRMaps; static {
		reducedRMaps = ImmutableList.<Map<Integer,Integer>>builder()
			.add( ImmutableMap.<Integer,Integer>builder()
					.put(Integer.valueOf("00001000100",2), Integer.valueOf("10001100100",2))
					.build() )
			.add( ImmutableMap.<Integer,Integer>builder()
					.put(Integer.valueOf("00001000100",2), Integer.valueOf("10001100100",2))
					.build() )
			.add( ImmutableMap.<Integer,Integer>of() )
			.add( ImmutableMap.<Integer,Integer>builder()
					.put(Integer.valueOf("00000010000",2), Integer.valueOf("00010001011",2))
					.build() )
			.add( ImmutableMap.<Integer,Integer>builder()
					.put(Integer.valueOf("00001000100",2), Integer.valueOf("10001000100",2))
					.put(Integer.valueOf("00001100100",2), Integer.valueOf("00000110000",2))
					.build() )
			.add( ImmutableMap.<Integer,Integer>builder()
					.put(Integer.valueOf("01110000000",2), Integer.valueOf("00000001000",2))
					.put(Integer.valueOf("01100000000",2), Integer.valueOf("01110000000",2))
					.build() )
			.add( ImmutableMap.<Integer,Integer>of() )
			.add( ImmutableMap.<Integer,Integer>builder()
					.put(Integer.valueOf("00000010000",2), Integer.valueOf("00010001011",2))
					.build() )
			.add( ImmutableMap.<Integer,Integer>builder()
					.put(Integer.valueOf("00000100000",2), Integer.valueOf("00000010000",2))
					.put(Integer.valueOf("00000010001",2), Integer.valueOf("00000010001",2))
					.build() )
			.add( ImmutableMap.<Integer,Integer>builder()
					.put(Integer.valueOf("00000100000",2), Integer.valueOf("00000010000",2))
					.put(Integer.valueOf("00000010001",2), Integer.valueOf("00000010001",2))
					.build() )
			.add( ImmutableMap.<Integer,Integer>builder()
					.put(Integer.valueOf("00001000100",2), Integer.valueOf("10001100100",2))
					.put(Integer.valueOf("00000010001",2), Integer.valueOf("00000010001",2))
					.put(Integer.valueOf("01110000000",2), Integer.valueOf("01110001000",2))
					.put(Integer.valueOf("00001100100",2), Integer.valueOf("00000010000",2))
					.put(Integer.valueOf("01101000100",2), Integer.valueOf("01100000000",2))
					.put(Integer.valueOf("00010011011",2), Integer.valueOf("00010001010",2))
					.put(Integer.valueOf("01110001011",2), Integer.valueOf("00000000011",2))
					.build() )
			.build();
	}
	
	private Dynamic d;
	private BeforeStates[] befores;
	
	@Before
	public void setUpD() {
		d = new Dynamic(11,Iterables.transform(Arrays.asList(input), new Function<String,Integer>(){
			@Override public Integer apply(String from) {
				return Integer.parseInt(from, 2);
			}			
		}));
		befores = d.get();
	}
	
	@Test
	public void testUtilsNonInhibition() {
		for (int i=0;i<rbars.length;i++) assertEquals(rbars[i],calcNonInhibition(bitmask[i], befores[i]));
	}
	
	@Test
	public void testUtilsNonActivation() {
		for (int i=0;i<gbars.length;i++) assertEquals(gbars[i],calcNonActivation(befores[i], rbars[i]));		
	}
	
	@Test
	public void testUtilsActivation() {
		for (int i=0;i<gs.length;i++) {
			assertEquals(gs[i],calcActivation(befores[i], gbars[i]));
			assertEquals(reducedGSize[i], befores[i].reducedActivators().size());
			assertEquals(reducedGs.get(i), befores[i].reducedActivators());
		}
	}
	
	@Test
	public void testUtilsInhibition() {
		for (int i=0; i<rs.length; i++) {
			int rbar = calcNonInhibition(befores[i]);
			int gbar = calcNonActivation(befores[i], rbar);
			int g = calcActivation(befores[i], gbar);
			assertEquals(rs[i], calcInhibition(befores[i], g, gbar));
			assertEquals(reducedRs.get(i), befores[i].requiredInhibitors());
			System.out.println();
			if (!reducedRMaps.get(i).equals(befores[i].mixedTerms())) {
				System.out.println(i+" : "+Integer.toBinaryString(gbar));
				for (Entry<Integer,Integer> e : reducedRMaps.get(i).entrySet()) System.out.println(Integer.toBinaryString(e.getKey())+" => "+Integer.toBinaryString(e.getValue()));
				System.out.println("vs");
				for (Entry<Integer,Integer> e : befores[i].mixedTerms().entrySet()) System.out.println(Integer.toBinaryString(e.getKey())+" => "+Integer.toBinaryString(e.getValue()));

			}
			assertEquals(reducedRMaps.get(i), befores[i].mixedTerms());
		}
	}
	
	@Test
	public void testUtilsMoreThan() {
		for (int i=0; i<bitmask.length-1; i++)  {
			assertEquals(Integer.bitCount(moreThan(i)),i+1);
			assertTrue(BankerOrder.INTEGER.INSTANCE.compare(moreThan(i), moreThan(i) << 1) < 0);
		}
	}
	
	@Test
	public void testUtilsLessThan() {
		for (int i = 2; i<bitmask.length; i++) {
			assertEquals(Integer.bitCount(lessThan(i)),i-1);
			assertTrue(BankerOrder.INTEGER.INSTANCE.compare(lessThan(i), lessThan(i)>>>1)>0);
		}
	}
			
	
}
